package com.springmvc.context;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.springmvc.stereotype.Controller;
import com.springmvc.web.bind.annotation.RequestMapping;
import com.springmvc.web.bind.annotation.RequestMethod;
import com.springmvc.web.constant.Const;
import com.springmvc.web.method.HandlerMethod;
import com.springmvc.web.servlet.HandlerAdapter;
import com.springmvc.web.servlet.HandlerInterceptor;
import com.springmvc.web.servlet.HandlerMapping;
import com.springmvc.web.servlet.mvc.method.RequestMappingInfo;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ioc容器
 */
public class ApplicationContext {

    private Map<String, Object> beanMap = new HashMap<>();


    public ApplicationContext(String xmlPath) {
        try {
            // 解析xml文件
            SAXReader reader = new SAXReader();
            Document document = reader.read(new File(xmlPath));
            // 组件扫描
            Element componentScanElement = (Element) document.selectSingleNode("/beans/component-scan");
            Map<RequestMappingInfo, HandlerMethod> map = componentScan(componentScanElement);
            // 创建视图解析器
            Element viewResolverElement = (Element) document.selectSingleNode("/beans/bean");
            createViewResolver(viewResolverElement);
            // 创建拦截器
            Element interceptorElement = (Element) document.selectSingleNode("/beans/interceptors");
            createInterceptors(interceptorElement);
            // 创建所有的HandlerMapping
            createHandlerMapping(Const.DEFAULT_PACKAGE, map);
            // 创建所有的HandlerAdapter
            createHandlerAdapter(Const.DEFAULT_PACKAGE);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    // 创建所有的HandlerAdapter
    private void createHandlerAdapter(String defaultPackage) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        String defaultPath = defaultPackage.replace(".", "/");
//        System.out.println("HandlerMapping的包路径---->"+defaultPath);
        String absolutPath = Thread.currentThread().getContextClassLoader().getResource(defaultPath).getPath();
        absolutPath = URLDecoder.decode(absolutPath, Charset.defaultCharset());
//        System.out.println("HandlerMapping的绝对路径---->"+absolutPath);
        File file = new File(absolutPath);
        File[] files = file.listFiles();
        for (File f : files) {
            String classFileName = f.getName();
            String simpleClassName = classFileName.substring(0, classFileName.indexOf("."));
            String className = defaultPackage + "." + simpleClassName;
            System.out.println("HandlerAdapter的全限定名---->" + className);
            Class aClass = Class.forName(className);
            // 只有实现了HandlerMapping接口的  这里才创建对象
            /**
             * isAssignableFrom是Java中的一个方法，用于检查一个类是否是另一个类的子类或者实现了某个接口
             */
            if(HandlerAdapter.class.isAssignableFrom(aClass)){
                // 创建对象
                Object bean = aClass.newInstance();
                // 存到IOC容器中
                beanMap.put(Const.HANDLER_ADAPTER, bean);
                return;
            }
        }
        System.out.println();
    }

    // 创建所有的HandlerMapping  这里就考虑一个
    private void createHandlerMapping(String defaultPackage, Map<RequestMappingInfo, HandlerMethod> map) throws InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException {
        String defaultPath = defaultPackage.replace(".", "/");
//        System.out.println("HandlerMapping的包路径---->"+defaultPath);
        String absolutPath = Thread.currentThread().getContextClassLoader().getResource(defaultPath).getPath();
        absolutPath = URLDecoder.decode(absolutPath, Charset.defaultCharset());
//        System.out.println("HandlerMapping的绝对路径---->"+absolutPath);
        File file = new File(absolutPath);
        File[] files = file.listFiles();
        for (File f : files) {
            String classFileName = f.getName();
            String simpleClassName = classFileName.substring(0, classFileName.indexOf("."));
            String className = defaultPackage + "." + simpleClassName;
            // System.out.println("HandlerMapping的全限定名---->" + className);
            Class aClass = Class.forName(className);
            // 只有实现了HandlerMapping接口的  这里才创建对象
            /**
             * isAssignableFrom是Java中的一个方法，用于检查一个类是否是另一个类的子类或者实现了某个接口
             */
            if(HandlerMapping.class.isAssignableFrom(aClass)){
                // 创建对象
                // Object bean = aClass.newInstance();  // 这是无参  现在改有参
                Constructor con = aClass.getDeclaredConstructor(Map.class);
                Object bean = con.newInstance(map);
                // 存到IOC容器中
                beanMap.put(Const.HANDLER_MAPPING, bean);
                return;
            }
        }

    }

    // 创建拦截器
    private void createInterceptors(Element interceptorElement) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // List集合存储拦截器对象
        List<HandlerInterceptor> interceptors = new ArrayList<>();
        // 获取拦截器列表
        List<Element> beans = interceptorElement.elements("bean");
        for (Element element : beans) {
            String className = element.attributeValue(Const.BEAN_TAF_CLASS_ATTRIBUTE);
//            System.out.println("拦截器的class值---->" + className);
            Class aClass = Class.forName(className);
            Object interceptor = aClass.newInstance();
            interceptors.add((HandlerInterceptor) interceptor);
        }
        // 存到IOC容器中  存的是 拦截器 集合
        beanMap.put(Const.INTERCEPTORS, interceptors);
    }

    // 创建视图解析器
    private void createViewResolver(Element viewResolverElement) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        // 视图解析器的全限定名
        String className = viewResolverElement.attributeValue(Const.BEAN_TAF_CLASS_ATTRIBUTE);
//        System.out.println("视图解析器名字---->" + className);
        // 通过反射创建视图解析器  这里就考虑默认的  加入到IOC容器中
        Class<?> aClass = Class.forName(className);
        // 视图解析器对象
        Object bean = aClass.newInstance();
        // 获取当前bean下的子节点   注意当前是视图bean    拿到的就是视图的前后缀
        List<Element> propertyElements = viewResolverElement.elements(Const.PROPERTY_TAG_NAME);
        for (Element propertyElement : propertyElements) {
            // 获取属性名
            String fieldName = propertyElement.attributeValue(Const.PROPERTY_NAME);
//            System.out.println("视图解析器的属性名字---->" + fieldName);
            // 属性名转换为set方法
            String setMethodName = fieldNameToSetMethodName(fieldName);
//            System.out.println("视图解析器的属性set方法---->" + setMethodName);
            // 获取属性值
            String fieldValue = propertyElement.attributeValue(Const.PROPERTY_VALUE);
//            System.out.println("视图解析器的属性值---->" + fieldValue);
            // 通过反射给属性赋值
            Method setMethod = aClass.getDeclaredMethod(setMethodName, String.class);
            setMethod.invoke(bean, fieldValue);
        }
//        beanMap.put(firstCharLowCase(aClass.getSimpleName()), bean);
        beanMap.put(Const.VIEW_RESOLVER, bean);
    }

    private String fieldNameToSetMethodName(String fieldName) {
        return "set" + firstCharUpperCase(fieldName);
    }

    private String firstCharUpperCase(String fieldName) {
        return fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }


    // 组件扫描   这里之做一层包扫描
    private Map<RequestMappingInfo, HandlerMethod> componentScan(Element componentScanElement) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 创建处理器映射器大Map
        HashMap<RequestMappingInfo, HandlerMethod> map = new HashMap<>();
        // 获取包名  xx.xx.xx
        String basePackage = componentScanElement.attributeValue(Const.BASE_PACKAGE);
//        System.out.println("组件扫描的包---->" + basePackage);
        String basePath = basePackage.replace(".", "/");
//        System.out.println("组件扫描的包对应的路径---->" + basePath);
        // 获取包的绝对路径
        String absolutePath = Thread.currentThread().getContextClassLoader().getResource(basePath).getPath();
        absolutePath = URLDecoder.decode(absolutePath, Charset.defaultCharset());
//        System.out.println("组件扫描的包对应的绝对路径---->" + absolutePath);
        // 封装为file对象
        File file = new File(absolutePath);
        File[] files = file.listFiles();
        for (File f : files) {
            String classFileName = f.getName();
//            System.out.println("组件扫描的包里面对应的文件名---->" + classFileName);
            if (classFileName.endsWith(Const.SUFFIX_CLASS)) {
                String simpleClassName = classFileName.substring(0, classFileName.lastIndexOf("."));
//                System.out.println("组件扫描的包里面class文件对应的简单类名---->" + simpleClassName);
                String className = basePackage + "." + simpleClassName;
//                System.out.println("组件扫描的包里面class文件对应的完整类名---->" + className);
                // 如果类上有@Controller注解，就实例化并存到IOC
                Class aClass = Class.forName(className);
                if (aClass.isAnnotationPresent(Controller.class)) {
                    Object bean = aClass.newInstance();
                    // 获取类名小写 这里就考虑默认的首字母小写情况
//                    System.out.println("组件扫描的包里面class文件对应的简单类名小写后---->" + firstCharLowCase(simpleClassName));
                    beanMap.put(firstCharLowCase(simpleClassName), bean);
                    // 获取这个bean中所有的handlerMethod,存到map中
                    Method[] methods = aClass.getDeclaredMethods();
                    for (Method method : methods) {
                        if ( method.isAnnotationPresent(RequestMapping.class)) {
                            // 获取RequestMapping注解
                            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                            // 获取RequestMapping注解的value
                            String[] requestURI = requestMapping.value();
                            // 获取RequestMapping注解的method
                            RequestMethod requestMethod = requestMapping.method();
                            // 创建 RequestMappingInfo
                            RequestMappingInfo requestMappingInfo = new RequestMappingInfo();
                            requestMappingInfo.setRequestURI(requestURI[0]);
                            requestMappingInfo.setMethod(requestMethod.toString());
                            // 创建HandlerMethod对象
                            HandlerMethod handlerMethod = new HandlerMethod();
                            handlerMethod.setHandler(bean);
                            handlerMethod.setMethod(method);
                            // 封装到map中
                            map.put(requestMappingInfo, handlerMethod);


                        }
                    }

                }
            }
        }
        return map;
    }

    private String firstCharLowCase(String simpleClassName) {
        return simpleClassName.substring(0, 1).toLowerCase() + simpleClassName.substring(1);
    }


    /**
     * 通过beanName获取bean
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        return beanMap.get(beanName);
    }
}
